package com.benew.ntt.ydeval;

import android.Manifest;
import android.content.pm.PackageManager;
import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaRecorder;
import android.text.TextUtils;
import android.util.Log;


import androidx.core.app.ActivityCompat;

import com.benew.ntt.ydeval.listener.RecordStreamListener;
import com.benew.ntt.ydeval.utils.EvalFileUtils;
import com.benew.ntt.ydeval.utils.PcmToWav;
import com.blankj.utilcode.util.Utils;
import com.ntt.core.nlogger.NLogger;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;


/**
 * 实现录音
 * Created by Android Studio.
 * User: tw
 * Date: 2022/1/11
 * Time: 6:29 下午
 */
public class CmAudioRecorder {
    //音频输入-麦克风
    private final static int AUDIO_INPUT = MediaRecorder.AudioSource.MIC;
    //采用频率
    //44100是目前的标准，但是某些设备仍然支持22050，16000，11025
    //采样频率一般共分为22.05KHz、44.1KHz、48KHz三个等级
    private final static int AUDIO_SAMPLE_RATE = 16000;
    //声道 单声道
    private final static int AUDIO_CHANNEL = AudioFormat.CHANNEL_IN_MONO;
    //编码
    private final static int AUDIO_ENCODING = AudioFormat.ENCODING_PCM_16BIT;


    // 缓冲区字节大小
    private int bufferSizeInBytes = 0;

    //录音对象
    private AudioRecord audioRecord;

    //录音状态
    private Status status = Status.STATUS_NO_READY;

    //文件名
    private String fileName;

    //录音文件
    private final List<String> filesName = new ArrayList<>();

    private RecordStreamListener listener;


    /**
     * 类级的内部类，也就是静态类的成员式内部类，该内部类的实例与外部类的实例
     * 没有绑定关系，而且只有被调用时才会装载，从而实现了延迟加载
     */
    private static class AudioRecorderHolder {
        /**
         * 静态初始化器，由JVM来保证线程安全
         */
        private static final CmAudioRecorder instance = new CmAudioRecorder();
    }

    private CmAudioRecorder() {
    }

    public static CmAudioRecorder getInstance() {
        return AudioRecorderHolder.instance;
    }

    /**
     * 创建录音对象
     */
    //@SuppressLint("MissingPermission")
    public void createAudio(String fileName, int audioSource, int sampleRateInHz, int channelConfig, int audioFormat) {
        if (ActivityCompat.checkSelfPermission(Utils.getApp(), Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {
            // TODO: Consider calling
            //    ActivityCompat#requestPermissions
            // here to request the missing permissions, and then overriding
            //   public void onRequestPermissionsResult(int requestCode, String[] permissions,
            //                                          int[] grantResults)
            // to handle the case where the user grants the permission. See the documentation
            // for ActivityCompat#requestPermissions for more details.
            return;
        }
        // 获得缓冲区字节大小
        bufferSizeInBytes = AudioRecord.getMinBufferSize(sampleRateInHz,
                channelConfig, channelConfig);
        audioRecord = new AudioRecord(audioSource, sampleRateInHz, channelConfig, audioFormat, bufferSizeInBytes);
        this.fileName = fileName;
    }

    /**
     * 创建默认的录音对象
     *
     * @param fileName 文件名
     */
    //@SuppressLint("MissingPermission")
    public void createDefaultAudio(String fileName) {
//        if (ActivityCompat.checkSelfPermission(Utils.getApp(), Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {
//            // TODO: Consider calling
//            //    ActivityCompat#requestPermissions
//            // here to request the missing permissions, and then overriding
//            //   public void onRequestPermissionsResult(int requestCode, String[] permissions,
//            //                                          int[] grantResults)
//            // to handle the case where the user grants the permission. See the documentation
//            // for ActivityCompat#requestPermissions for more details.
//            return;
//        }
        // 获得缓冲区字节大小
        bufferSizeInBytes = AudioRecord.getMinBufferSize(AUDIO_SAMPLE_RATE,
                AUDIO_CHANNEL, AUDIO_ENCODING);
        audioRecord = new AudioRecord(AUDIO_INPUT, AUDIO_SAMPLE_RATE, AUDIO_CHANNEL, AUDIO_ENCODING, bufferSizeInBytes);
        this.fileName = fileName;
        status = Status.STATUS_READY;
    }


    /**
     * 开始录音
     *
     * @param listener 音频流的监听
     */
    public void startRecord(final RecordStreamListener listener) {

        if (status == Status.STATUS_NO_READY || TextUtils.isEmpty(fileName)) {
            throw new IllegalStateException("录音尚未初始化,请检查是否禁止了录音权限~");
        }
        if (status == Status.STATUS_START) {
            NLogger.e("startRecord # 二次点击开始！！ 正在录音!!!");
            return;
        }
        this.listener = listener;
        if (listener != null) {
            listener.startRecord();
        }
        // 开始录音
        audioRecord.startRecording();
        new Thread(this::writeDataTOFile).start();

//        new Handler().postDelayed(this::setBeforeOutTime,300);


    }

    /**
     * 暂停录音
     */
    public void pauseRecord() {
        Log.d("AudioRecorder", "===pauseRecord===");
        if (status != Status.STATUS_START) {
            NLogger.e("pauseRecord #尚未开始录音！！！");
        } else {
            if (audioRecord != null) {
                audioRecord.stop();
            }
            status = Status.STATUS_PAUSE;
        }
    }

    /**
     * 停止录音
     */
    public void stopRecord() {
        Log.d("AudioRecorder", "===stopRecord===");
        if (status == Status.STATUS_NO_READY || status == Status.STATUS_READY) {
            NLogger.e("stopRecord #尚未开始录音！！！");
        } else {
            try {
                if (audioRecord != null) {
                    audioRecord.stop();
                }
            } catch (Exception e) {
                NLogger.e("stopRecord #录音停止失败！！！", e.getMessage());
            }

            status = Status.STATUS_STOP;
            release();
            if (listener != null) {
                listener.onEndRecord();
            }
        }
    }

    /**
     * 释放资源
     */
    public void release() {
        Log.d("AudioRecorder", "===release===");
        //假如有暂停录音
        try {
            if (filesName.size() > 0) {
                List<String> filePaths = new ArrayList<>();
                for (String fileName : filesName) {
                    filePaths.add(EvalFileUtils.getPcmFileAbsolutePath(fileName));
                }
                //清除
                filesName.clear();
                //将多个pcm文件转化为wav文件
                mergePCMFilesToWAVFile(filePaths);

            } else {
                //这里由于只要录音过filesName.size都会大于0,没录音时fileName为null
                //会报空指针 NullPointerException
                // 将单个pcm文件转化为wav文件
                //Log.d("AudioRecorder", "=====makePCMFileToWAVFile======");
                //makePCMFileToWAVFile();
            }
        } catch (IllegalStateException e) {
            e.printStackTrace();
            NLogger.e("release ==> " + e.getMessage());
        }
        if (audioRecord != null) {
            audioRecord.release();
            audioRecord = null;
        }

        status = Status.STATUS_NO_READY;
    }

    /**
     * 取消录音
     */
    public void cancels() {
        status = Status.STATUS_NO_READY;
        filesName.clear();
        if (audioRecord != null) {
            audioRecord.release();
            audioRecord = null;
        }


        // 删除掉原录音文件
        if (fileName != null) {
            File file = new File(EvalFileUtils.getPcmFileAbsolutePath(fileName + ".pcm"));
            if (file.exists()) {
                file.delete();
            }
        }
        fileName = null;

    }


    /**
     * 将音频信息写入文件
     * <p>
     * 音频流的监听
     */
    private void writeDataTOFile() {
        FileOutputStream fos = null;
        try {
            String currentFileName = fileName;
            if (status == Status.STATUS_PAUSE) {
                //假如是暂停录音 将文件名后面加个数字,防止重名文件内容被覆盖
                currentFileName += filesName.size();
            }
            filesName.add(currentFileName);
            File file = new File(EvalFileUtils.getPcmFileAbsolutePath(currentFileName));
            if (file.exists()) {
                file.delete();
            }
            fos = new FileOutputStream(file);// 建立一个可存取字节的文件
        } catch (IllegalStateException e) {
            Log.e("AudioRecorder", e.getMessage());
            throw new IllegalStateException(e.getMessage());
        } catch (FileNotFoundException e) {
            Log.e("AudioRecorder", e.getMessage());
        }
        // new一个byte数组用来存一些字节数据，大小为缓冲区大小
        byte[] audiodata = new byte[bufferSizeInBytes];
        int readsize;

//        short[] buffer = new short[bufferSizeInBytes];
//        int r;

        //将录音状态设置成正在录音状态
        status = Status.STATUS_START;
        while (status == Status.STATUS_START && audioRecord != null) {
            readsize = audioRecord.read(audiodata, 0, bufferSizeInBytes);

            if (AudioRecord.ERROR_INVALID_OPERATION != readsize && fos != null) {
                try {
                    fos.write(audiodata);
                } catch (IOException e) {
                    e.printStackTrace();
                }

                if (listener != null) {
                    //用于拓展业务
                    listener.recordOfByte(audiodata,
                            0,
                            audiodata.length,
                            getDecibelForPcm(audiodata, readsize));
                }
            }
        }
        try {
            if (fos != null) {
                fos.close();// 关闭写入流
            }
        } catch (IOException e) {
            Log.e("AudioRecorder", e.getMessage());
        }
    }


    /**
     * 获取分贝值
     *
     * @param buffer
     * @param readsize
     * @return
     */
    private double getDecibelForPcm(byte[] buffer, int readsize) {
        long sum = 0;
        long temp;
        for (int i = 0; i < buffer.length; i += 2) {
            temp = (buffer[i + 1] * 128 + buffer[i]); //累加求和
            temp *= temp;
            sum += temp;
        }

        //平方和除以数据长度，得到音量大小
        double square = sum / (double) readsize; //音量大小
        double result = 10 * Math.log10(square * 2); //分贝值
        return result;
    }


    private double formatDouble(double d) {
        try {
            String format = new DecimalFormat("0.000").format(d);
            NLogger.e("format ==> " + format);
            return Double.parseDouble(format);
        } catch (Exception e) {
            NLogger.e(e.getMessage());
            return d;
        }

    }


    /**
     * 将pcm合并成wav
     *
     * @param filePaths
     */
    private void mergePCMFilesToWAVFile(final List<String> filePaths) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                String wavFileAbsolutePath = EvalFileUtils.getWavFileAbsolutePath(fileName);
                if (PcmToWav.mergePCMFilesToWAVFile(filePaths, wavFileAbsolutePath)) {
                    //操作成功
                    NLogger.e("pcm合并成wav 成功！");
                    if (listener != null) {
                        listener.recordPath(wavFileAbsolutePath);
                    }
                } else {
                    //操作失败
                    NLogger.e("格式转换失败");
                }
                fileName = null;
            }
        }).start();
    }

    /**
     * 将单个pcm文件转化为wav文件
     */
    private void makePCMFileToWAVFile() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                if (PcmToWav.makePCMFileToWAVFile(EvalFileUtils.getPcmFileAbsolutePath(fileName), EvalFileUtils.getWavFileAbsolutePath(fileName), true)) {
                    //操作成功
                } else {
                    //操作失败
                    Log.e("AudioRecorder", "makePCMFileToWAVFile fail");
                    throw new IllegalStateException("makePCMFileToWAVFile fail");
                }
                fileName = null;
            }
        }).start();
    }

    /**
     * 获取录音对象的状态
     *
     * @return
     */
    public Status getStatus() {
        return status;
    }

    /**
     * 获取本次录音文件的个数
     *
     * @return
     */
    public int getPcmFilesCount() {
        return filesName.size();
    }

    /**
     * 录音对象的状态
     */
    public enum Status {
        //未开始
        STATUS_NO_READY,
        //预备
        STATUS_READY,
        //录音
        STATUS_START,
        //暂停
        STATUS_PAUSE,
        //停止
        STATUS_STOP
    }

    public void setListener(RecordStreamListener listener) {
        this.listener = listener;
    }
}
